using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SilkSharp.src
{
	/*
	 * processing of gains.
	 * 
	 * @author Dingxin Xu
	 */
	public class Silk_process_gains_FLP
	{
		/*
		 * Processing of gains.
		 * @param psEnc Encoder state FLP
		 * @param psEncCtrl Encoder control FLP
		 */
		static void SKP_Silk_process_gains_FLP(
			SKP_Silk_encoder_state_FLP psEnc,             /* I/O  Encoder state FLP                       */
			SKP_Silk_encoder_control_FLP psEncCtrl          /* I/O  Encoder control FLP                     */
		)
		{
			SKP_Silk_shape_state_FLP psShapeSt = psEnc.sShape;
			int k;
			int[] pGains_Q16 = new int[ Silk_define.NB_SUBFR ];
			float s, InvMaxSqrVal, gain;

			/* Gain reduction when LTP coding gain is high */
			if( psEncCtrl.sCmn.sigtype == Silk_define.SIG_TYPE_VOICED )
			{
				s = 1.0f - 0.5f * Silk_SigProc_FLP.SKP_sigmoid( 0.25f * ( psEncCtrl.LTPredCodGain - 12.0f ) );
				for( k = 0; k < Silk_define.NB_SUBFR; k++ )
				{
					psEncCtrl.Gains[ k ] *= s;
				}
			}

			/* Limit the quantized signal */
			InvMaxSqrVal = (float)( Math.Pow( 2.0f, 0.33f * ( 21.0f - psEncCtrl.current_SNR_dB ) ) / psEnc.sCmn.subfr_length );

			for( k = 0; k < Silk_define.NB_SUBFR; k++ )
			{
				/* Soft limit on ratio residual energy and squared gains */
				gain = psEncCtrl.Gains[ k ];
				gain = (float)Math.Sqrt( gain * gain + psEncCtrl.ResNrg[ k ] * InvMaxSqrVal );
				psEncCtrl.Gains[ k ] = ( gain < 32767.0f ? gain : 32767.0f );
			}

			/* Prepare gains for noise shaping quantization */
			for( k = 0; k < Silk_define.NB_SUBFR; k++ )
			{
				pGains_Q16[ k ] = (int)( psEncCtrl.Gains[ k ] * 65536.0f );
			}

			/* Noise shaping quantization */
			int[] LastGainIndex_ptr = new int[ 1 ];
			LastGainIndex_ptr[ 0 ] = psShapeSt.LastGainIndex;
			Silk_gain_quant.SKP_Silk_gains_quant( psEncCtrl.sCmn.GainsIndices, pGains_Q16,
					LastGainIndex_ptr, psEnc.sCmn.nFramesInPayloadBuf );
			psShapeSt.LastGainIndex = LastGainIndex_ptr[ 0 ];
			/* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */
			for( k = 0; k < Silk_define.NB_SUBFR; k++ )
			{
				psEncCtrl.Gains[ k ] = pGains_Q16[ k ] / 65536.0f;
			}

			/* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */
			if( psEncCtrl.sCmn.sigtype == Silk_define.SIG_TYPE_VOICED )
			{
				if( psEncCtrl.LTPredCodGain + psEncCtrl.input_tilt > 1.0f )
				{
					psEncCtrl.sCmn.QuantOffsetType = 0;
				}
				else
				{
					psEncCtrl.sCmn.QuantOffsetType = 1;
				}
			}

			/* Quantizer boundary adjustment */
			if( psEncCtrl.sCmn.sigtype == Silk_define.SIG_TYPE_VOICED )
			{
				psEncCtrl.Lambda = 1.2f - 0.4f * psEnc.speech_activity
										 - 0.3f * psEncCtrl.input_quality
										 + 0.2f * psEncCtrl.sCmn.QuantOffsetType
										 - 0.1f * psEncCtrl.coding_quality;
			}
			else
			{
				psEncCtrl.Lambda = 1.2f - 0.4f * psEnc.speech_activity
										 - 0.4f * psEncCtrl.input_quality
										 + 0.4f * psEncCtrl.sCmn.QuantOffsetType
										 - 0.1f * psEncCtrl.coding_quality;
			}

			//assert( psEncCtrl.Lambda >= 0.0f );
			//assert( psEncCtrl.Lambda <  2.0f );
		}
	}
}
